#include "g_graphics.h"

#include "xformat.h"
#include "graphics_window.h"
#include "xstring.h"
#include "xrt_error.h"
#include "xsys_log.h"
#include "xsync.h"
#include "xdata_structure.h"
// Here we will implement the default Graphics2D 
// Typically taking advantage of the D3D8 device
// as our default device to implement

// Please make sure that you have obtained DirectX SDK
#include <d3d8.h>
#include <d3dx8.h>
#include <windows.h>

namespace {

  using xge::String;
  using xge::Point;
  using namespace xge::window;
  using namespace xge::graphics;
  using xge::Allocator;
  using xge::xge_internal::ConvertAnsiStr2Unicode;

  class D3D8GraphicsDevice;
  class D3D8GraphicsWindow;
  class D3D8Texture;
  class D3D8RenderTexture;
  class D3D8RenderTarget;


  static D3D8GraphicsDevice* gDevice = NULL;
  // This piece of code can be used to dynamically linked
  // by PlugIn Manager as our default stuff to create  ,
  // also, it can also directly linked to the release program

  enum {
    VERTEX_BUFFER_SIZE = 8000,
    PRIMITIVE_QUAD_SIZE= 4 ,
    PRIMITIVE_LINE_SIZE= 2
  };

  // Here we implement a default window here on windows 
  // After creating this class  , we will get a default
  // windows window , which we can obtain the HWND and 
  // message
  enum {
    CANT_CREATE_WINDOWS =1,
    CANT_REGISTER_CLASS =2,
    CANT_CREATE_D3D =3,
    CANT_CREATE_D3DDEVICE =4,
    CANT_CREATE_INDEX_BUFFER =5,
    CANT_CREATE_VERTEX_BUFFER =6,
    CANT_BUILD_PARAMETER =7,
    CANT_GET_SURFACE =8,
    CANT_BEGIN_SCENE =9,
    DEVICE_LOST =10,
    STATUS_NORMAL = -1
  };

  class D3D8GraphicsWindow: public xge::window::GraphicsWindow {
  public:
    virtual bool Snapshot( const String& file_path );
    virtual void NotifyCaptionName( const char_t* caption );
    virtual void GetPlatformHandler( void** out_argument ) {
      *out_argument = (void*)(hWnd_);
    }
    // default windows procedure
    static LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam );
  public:
    int Create(
      size_t width , size_t height ,
      const String& caption_,
      const String& icon_resource,
      bool fullscreen
      );
    void Destroy();
    // Initialized the D3D8GraphicsWindow
    D3D8GraphicsWindow( D3D8GraphicsDevice* device ) : device_(device) {}

    HWND hwnd() const {
      return hWnd_;
    }

    HINSTANCE hinstance() const {
      return hInstance_;
    }

    void InvokeFocusLost() {
      if( focus_change_listener_ != NULL )
        focus_change_listener_->OnFocusLost();
    }

    void InvokeFocusGain() {
      if( focus_change_listener_ != NULL )
        focus_change_listener_->OnFocusGain();
    }

    void InvokeOnExit() {
      if( on_exit_listener_ != NULL )
        on_exit_listener_->OnExit();
    }
  private:

    HINSTANCE hInstance_;
    HWND hWnd_;
    D3D8GraphicsDevice* device_;
  };


  int D3D8GraphicsWindow::Create
    ( size_t width , size_t height , const String& caption_, const String& icon_resource, bool fullscreen ) {
      hInstance_ = GetModuleHandle(0);
      // Create the window now 
#ifdef XGE_USE_WIDE_CHAR
      WNDCLASSW 
#else
      WNDCLASSA
#endif
        winclass;
      winclass.style = 0;
      winclass.lpfnWndProc	= WndProc;
      winclass.cbClsExtra		= 0;
      winclass.cbWndExtra		= 0;
      winclass.hInstance		= hInstance_;
      winclass.hCursor		= LoadCursor(NULL, IDC_ARROW);
      winclass.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);
      winclass.lpszMenuName	= NULL; 
      winclass.lpszClassName	= _xT("XGE_D3D8_Device");
      if( icon_resource.empty() )
        winclass.hIcon = 
#ifdef XGE_USE_WIDE_CHAR
        LoadIconW(NULL,IDI_APPLICATION);
#else
        LoadIconA(NULL,IDI_APPLICATION);
#endif
      else
        winclass.hIcon = 
#ifdef XGE_USE_WIDE_CHAR
        LoadIconW(hInstance_,icon_resource.c_str());
#else
        LoadIconA(hInstance_,icon_resource.c_str());
#endif
      // register the windows class now
      if( 
#ifdef XGE_USE_WIDE_CHAR
        RegisterClassW(&winclass) 
#else
        RegisterClassA(&winclass)
#endif
        == FALSE ) {
        WIN32_API_WARNING( _xT("RegisterClassW") );
        // doing the return now
        return CANT_REGISTER_CLASS;
      }
      // doing the fullscreen stuff here
      set_fullscreen(fullscreen);
      if( fullscreen ) {
        hWnd_ = 
#ifdef XGE_USE_WIDE_CHAR
          CreateWindowExW(
          WS_EX_TOPMOST,
          _xT("XGE_D3D8_Device"),
          caption_.c_str(),
          WS_POPUP|WS_VISIBLE,
          0,0,0,0,
          NULL,NULL,hInstance_,NULL
          );
#else
          CreateWindowExA(
          WS_EX_TOPMOST,
          _xT("XGE_D3D8_Device"),
          caption_.c_str(),
          WS_POPUP|WS_VISIBLE,
          0,0,0,0,
          NULL,NULL,hInstance_,NULL
          );
#endif
        // show the size of this windows 
        RECT rect;
        ::GetClientRect(hWnd_,&rect);
        set_width( rect.right );
        set_height( rect.bottom );
      } else {

        // get the client size
        RECT clientSize;
        clientSize.top = 0;
        clientSize.left = 0;
        clientSize.right = width ;
        clientSize.bottom = height;
        AdjustWindowRect(&clientSize, WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_VISIBLE, FALSE);
        const int realWidth = clientSize.right - clientSize.left;
        const int realHeight = clientSize.bottom - clientSize.top;

        set_width( width );
        set_height( height );

        int windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2;
        int windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2;

        if ( windowLeft < 0 )
          windowLeft = 0;
        if ( windowTop < 0 )
          windowTop = 0;	

        hWnd_ = 
#ifdef XGE_USE_WIDE_CHAR
          CreateWindowExW(
          0,
          _xT("XGE_D3D8_Device"),
          caption_.c_str(),
          WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_VISIBLE,
          windowLeft, windowTop , realWidth , realHeight,
          NULL,NULL,hInstance_,NULL
          );
#else
          CreateWindowExA(
          0,
          _xT("XGE_D3D8_Device"),
          caption_.c_str(),
          WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_VISIBLE,
          windowLeft, windowTop , realWidth , realHeight,
          NULL,NULL,hInstance_,NULL
          );
#endif

      }

        if( hWnd_ == NULL ) {
          WIN32_API_WARNING("CreateWindowExW");
          return CANT_CREATE_WINDOWS;
        }

        ShowWindow(hWnd_, SW_SHOW);
        UpdateWindow(hWnd_);
        return STATUS_NORMAL ;
      }


  void D3D8GraphicsWindow::Destroy() {
    if( hWnd_ != NULL ) {
      DestroyWindow(hWnd_);
      hWnd_ = NULL;
    }
  }

  void D3D8GraphicsWindow::NotifyCaptionName( const char_t* caption ) {
#ifdef XGE_USE_WIDE_CHAR
    SetWindowTextW(hWnd_,caption);
#else
    SetWindowTextA(hWnd_,caption);
#endif
  }
  // This error collector will help to
  // record the error in the D3D8Device
  // and it will make the returned error
  // code to become the valid string to
  // be output !
  class D3D8ErrorCollector {
  public:
    static const char_t* MakeOutputErrorString( HRESULT error_code ) {
      OutputErrorStringMap::iterator ret = kErrorMap->find(error_code);
      return ret == kErrorMap->end() ? _xT("Unreferenced Error Code!") : (*ret).second;
    }
    static void InitializeOutputErrorString();
    static void Destroy() {
      XASSERT(kErrorMap);
      delete kErrorMap;
      kErrorMap = NULL;
    }
    typedef xge::map< HRESULT , const char_t* >::Map OutputErrorStringMap;
  private:
    static OutputErrorStringMap* kErrorMap;
  };

  D3D8ErrorCollector::OutputErrorStringMap* D3D8ErrorCollector::kErrorMap = NULL;

  void D3D8ErrorCollector::InitializeOutputErrorString() {
    // the following code should be modified through the target
    // D3D8 device header !
    XASSERT(kErrorMap==NULL);
    kErrorMap = new OutputErrorStringMap();
#define MAKE_ERROR_STRING(Key,Value) \
  do{ XASSERT( kErrorMap->insert( std::make_pair( Key , _xT(Value)) ).second ); }while(0)
    // Now insert the error string into the map
    MAKE_ERROR_STRING(D3DERR_WRONGTEXTUREFORMAT,"Wrong Texture Format!");
    MAKE_ERROR_STRING(D3DERR_UNSUPPORTEDCOLOROPERATION,"Unsupported Color Operation!");
    MAKE_ERROR_STRING(D3DERR_UNSUPPORTEDCOLORARG,"Unsupported Color ARG!");
    MAKE_ERROR_STRING(D3DERR_UNSUPPORTEDALPHAOPERATION,"Unsupported Alpha Operation!");
    MAKE_ERROR_STRING(D3DERR_UNSUPPORTEDALPHAARG,"Unsupported ALpha ARG!");
    MAKE_ERROR_STRING(D3DERR_TOOMANYOPERATIONS,"Too Many Operations!");
    MAKE_ERROR_STRING(D3DERR_CONFLICTINGTEXTUREFILTER,"Conflicting Texture Filter!");
    MAKE_ERROR_STRING(D3DERR_UNSUPPORTEDFACTORVALUE,"Unsupported Fator Value!");
    MAKE_ERROR_STRING(D3DERR_CONFLICTINGRENDERSTATE,"Conflicting Render State!");
    MAKE_ERROR_STRING(D3DERR_UNSUPPORTEDTEXTUREFILTER,"Unsupported Texture Filter!");
    MAKE_ERROR_STRING(D3DERR_CONFLICTINGTEXTUREPALETTE,"Conflicting Texture Pallette!");
    MAKE_ERROR_STRING(D3DERR_DRIVERINTERNALERROR,"Driver Internal Error!");
    MAKE_ERROR_STRING(D3DERR_NOTFOUND,"Not Found!");
    MAKE_ERROR_STRING(D3DERR_MOREDATA,"More Data!");
    MAKE_ERROR_STRING(D3DERR_DEVICELOST,"Device Lost!");
    MAKE_ERROR_STRING(D3DERR_DEVICENOTRESET,"Device Not Reset!");
    MAKE_ERROR_STRING(D3DERR_NOTAVAILABLE,"Not Available!");
    MAKE_ERROR_STRING(D3DERR_OUTOFVIDEOMEMORY,"Outof Vedio Memory!");
    MAKE_ERROR_STRING(D3DERR_INVALIDDEVICE,"Invalid Device!");
    MAKE_ERROR_STRING(D3DERR_INVALIDCALL,"Invalid Call!");
    MAKE_ERROR_STRING(D3DERR_DRIVERINVALIDCALL,"Driver Invalid Call!");
    MAKE_ERROR_STRING(D3D_OK,"Status OK!");
#undef MAKE_ERROR_STRING
  }

  // Now we simply output some useful macro for log the
  // D3D information or errors 

#define OUTPUT_D3D_INFO(Level,Flush,ApiName,DescriptionString) \
  SYS_LOG(Level,Flush,_xT("D3D8 Api Name:%s.\nDescription:%s.\n"),ApiName,DescriptionString)

  class D3D8GraphicsDevice : public Graphics2D {
  public:

    virtual Texture* CreateTexture( void* raw_buffer , size_t size , const Color& color_key );
    virtual Texture* CreateTexture( size_t width , size_t height ) ;
    virtual bool DestroyTexture( Texture* texture );
    virtual RenderTarget* CreateRenderTarget( size_t width , size_t height );
    virtual bool DestroyRenderTarget( RenderTarget* target ) ;
    virtual void RenderQuad( const Quad& quad );
    virtual void RenderLine( const Point& p1,const Point& p2,float zbuffer , int blending , const Color& color1, const Color& color2 );
    virtual int BeginScene( int mode  , int primitive_type , RenderTarget* target , 
      Texture* texture , int blending_mode ,size_t wait_time_out );
    virtual int EndScene(  bool sync ) ;
    virtual void SetClippingArea( const Rect& rect );
    virtual void SetGlobalTransformation( 
      float ax,
      float ay,
      float horizontal_offset ,
      float vertical_offset,
      float horizontal_scale,
      float vertical_scale,
      float rotation
      ) ;
    virtual void Clear( const Color& color );
    virtual GraphicsWindow* GetGraphicsWindow() {
      return window_;
    }
  public:
    IDirect3DDevice8* GetDevice() const {
      return device_; 
    }
    D3DPRESENT_PARAMETERS* GetPresentParemeter() const {
      return const_cast<D3DPRESENT_PARAMETERS*>(&present_par_);
    }
  public:
    static D3D8GraphicsDevice& GetSingleton();
    D3D8GraphicsDevice( size_t  width, size_t height , bool fullscreen , const String& caption , const String& resource_title );
    ~D3D8GraphicsDevice();
    bool Snapshot( const char_t* file_name );
  public:
    int  CreateGraphics();
    void DestroyGraphics();
    void AdjustWindow(){}
    void DeviceRestore();
    void AdjustPresentParameter();
    void BuildProjection( size_t width , size_t height );
    void SetProjection();
    void SetRenderState();
    int  InitRenderingBuffer();
    void SetBlendingMode(int mode);
    void SetTexture( Texture* texture );
    int  SetRenderTarget( RenderTarget* target );
    void Batch( bool end );
    void OnRestore();
    void OnResize( size_t width , size_t height );


    int last_status_code() const {
      return last_status_code_;
    }
  private:
    // interface of D3D
    IDirect3D8* direct3d8_;
    IDirect3DDevice8* device_;
    // presentation parameter
    D3DPRESENT_PARAMETERS present_par_;
    // affine-transformation
    D3DXMATRIX matrix_projection_;
    D3DXMATRIX matrix_view_;
    // internal rendering buffer
    IDirect3DIndexBuffer8 * ibuffer_;
    IDirect3DVertexBuffer8* vbuffer_;
    IDirect3DSurface8* screen_surface_;
    IDirect3DSurface8* screen_depth_;
    // windows
    D3D8GraphicsWindow* window_;
    // running data 
    D3D8Texture* current_texture_;
    int current_blend_;
    D3D8RenderTarget * current_target_;
    // render gate
    Vertex* vertex_buffer_ptr_;
    size_t vertex_buffer_num_;
    // current rendering type
    int cur_primitive_type_;
    int cur_blend_mode_;
    int cur_mode_;
    // status code 
    int last_status_code_;
  };

  //====================== Implementation of D3D8GraphicsDevice=========================================

  int D3D8GraphicsDevice::CreateGraphics() {
    HRESULT hResult;
    direct3d8_ = Direct3DCreate8( D3D_SDK_VERSION );
    if( direct3d8_ == NULL ) {
      // error 
      return CANT_CREATE_D3D;
    }
    // Log the information 
    D3DADAPTER_IDENTIFIER8 adapterID8;
    direct3d8_->GetAdapterIdentifier(D3DADAPTER_DEFAULT,D3DENUM_NO_WHQL_LEVEL,&adapterID8);

    char_t driver_name[512];
    char_t description[512];
    // since the output char is used ANSI-8 string
    // we should change it to the default char
    if( ConvertAnsiStr2Unicode(adapterID8.Driver,driver_name,512) <=0 ||
        ConvertAnsiStr2Unicode(adapterID8.Description,description,512)<=0 ) {
          driver_name[0]= char_t();
          description[0]= char_t();
          SYS_LOG(WARNING,false,_xT("Convert adapter information encounter error!"));
    }
    SYS_LOG(INFO,false,_xT("Direct3D Driver Name: %s .\n")
      _xT("Description: %s.\n")
      _xT("Version: %d , %d , %d , %d."),
      driver_name,
      description,
      HIWORD(adapterID8.DriverVersion.HighPart),
      LOWORD(adapterID8.DriverVersion.HighPart),
      HIWORD(adapterID8.DriverVersion.LowPart),
      LOWORD(adapterID8.DriverVersion.LowPart) );

    D3DDISPLAYMODE displayMode;
    hResult = direct3d8_->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&displayMode) ;
    if( FAILED(hResult) ){
        // Output the d3d information(error)
        OUTPUT_D3D_INFO(PANIC,true,_xT("GetAdapterDisplayMode"),D3D8ErrorCollector::MakeOutputErrorString(hResult));
        return CANT_BUILD_PARAMETER;
    }
    if( displayMode.Format == D3DFMT_UNKNOWN ) {
      SYS_LOG(PANIC,true,_xT("Can not get the desktop format or not supported by current driver!"));
      // Since if the the window mode is fullscreen
      // there is definitely that we can not set up
      // windowed mode
      if( !window_->fullscreen() ) {
        SYS_LOG(PANIC,true,_xT("Window mode : can not setup !"));
        return CANT_BUILD_PARAMETER;
      }
    }

    ZeroMemory( &present_par_ , sizeof(D3DPRESENT_PARAMETERS) );
    if( !window_->fullscreen() ) {
      present_par_.Windowed = TRUE;
      present_par_.BackBufferCount =1;
      present_par_.BackBufferWidth = window_->width();
      present_par_.BackBufferHeight= window_->height();
      present_par_.BackBufferFormat= displayMode.Format;
      present_par_.hDeviceWindow = window_->hwnd();
      present_par_.EnableAutoDepthStencil = TRUE;
      present_par_.AutoDepthStencilFormat = D3DFMT_D16;
      present_par_.SwapEffect = D3DSWAPEFFECT_COPY;
      present_par_.MultiSampleType = D3DMULTISAMPLE_NONE;
    } else {
      // set up fullscreen display presentation parameter
      present_par_.Windowed = FALSE;
      present_par_.BackBufferCount =1;
      present_par_.BackBufferWidth = window_->width();
      present_par_.BackBufferHeight= window_->height();
      // This format is used by the irrlicht
      present_par_.BackBufferFormat= D3DFMT_X8R8G8B8;
      present_par_.hDeviceWindow = window_->hwnd();
      present_par_.EnableAutoDepthStencil = TRUE;
      present_par_.AutoDepthStencilFormat = D3DFMT_D16;
      present_par_.SwapEffect = D3DSWAPEFFECT_COPY;
      // We do not force the VSYNC ( since it is useless )
      present_par_.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; 
      present_par_.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;

    }
    // Here,we will set up our device internally now
    hResult = direct3d8_->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,window_->hwnd(),
      D3DCREATE_HARDWARE_VERTEXPROCESSING,&present_par_,&device_) ;
    if( FAILED(hResult) ) {
        // We can not create the d3d hardware vertex processing device 
        // the current graphics card seems to be not support this type
        // of device
        OUTPUT_D3D_INFO(PANIC,true,_xT("IDirect3D8::CreateDevice!\nHardware Vertex Processing!") \
          ,D3D8ErrorCollector::MakeOutputErrorString(hResult));
        hResult = direct3d8_->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,window_->hwnd(),
          D3DCREATE_MIXED_VERTEXPROCESSING,&present_par_,&device_);
        if( FAILED( hResult ) ) {
            // We can not create the d3d mixed vertex processing device
            // the current graphics card seems to be not support this type
            OUTPUT_D3D_INFO(PANIC,true,_xT("IDirect3D8::CreateDevice!\nMixed Vertex Processing!"), \
              D3D8ErrorCollector::MakeOutputErrorString(hResult));

            hResult = direct3d8_->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,window_->hwnd(),
              D3DCREATE_SOFTWARE_VERTEXPROCESSING,&present_par_,&device_);
            if( FAILED( hResult ) ) {
                // We can not create the d3d pure software vertex processing device
                // we have no idea , but to using low bpp and other way to make the
                // current device work 
              OUTPUT_D3D_INFO(PANIC,true,_xT("IDirect3D8::CreateDevice!\nSoftware Vertex Processing!"), \
                D3D8ErrorCollector::MakeOutputErrorString(hResult));
                AdjustPresentParameter();

                hResult = direct3d8_->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,window_->hwnd(),
                  D3DCREATE_SOFTWARE_VERTEXPROCESSING,&present_par_,&device_);
                if( FAILED( hResult ) ) { 
                  OUTPUT_D3D_INFO(PANIC,true,_xT("IDirect3D8::CreateDevice!\nSoftware Vertex Processing(AND)16 Color Bit!"), \
                    D3D8ErrorCollector::MakeOutputErrorString(hResult));
                    return CANT_CREATE_D3DDEVICE;
                }
            }
        }
    }
    // get the surface and depth buffer
    device_->GetDepthStencilSurface(&screen_depth_);
    device_->GetRenderTarget(&screen_surface_);
    SYS_LOG(INFO,true,_xT("Create D3D8 render device ok!"));
    // adjust the window stuff here
    AdjustWindow();
    // set up the projection
    BuildProjection(window_->width(),window_->height());
    SetProjection();
    // initialize the buffer 
    int ret = InitRenderingBuffer();
    if( ret != STATUS_NORMAL ) 
      return ret;
    SetRenderState();
    // clear the whole stuff 
    SetTexture(NULL);
    Clear( Color::kBlack );
    return STATUS_NORMAL;
  }

  void D3D8GraphicsDevice::DestroyGraphics() {
    if( screen_depth_ != NULL )
      screen_depth_->Release();
    if( screen_surface_ != NULL )
      screen_depth_->Release();
    if( ibuffer_ != NULL )
      ibuffer_->Release();
    if( vbuffer_ != NULL )
      vbuffer_->Release();
    if( device_ != NULL )
      device_->Release();
    if( direct3d8_ != NULL )
      direct3d8_->Release();
  }

  void D3D8GraphicsDevice::AdjustPresentParameter() {
    // Now lets do the low past testing here
    // We try to set up the color as 16 bit
    // using the format as D3DFMT_A1R5G5B5 to create
    // if not so , ok , we simply treat the card
    // is TOO OLD ! The user should purchase a new
    // computer now.
    present_par_.BackBufferFormat = D3DFMT_A1R5G5B5;
    // Now the color bits is 16 not 32( default )
  }

  void D3D8GraphicsDevice::SetRenderState() {
    device_->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
    device_->SetRenderState( D3DRS_LIGHTING, FALSE );
    device_->SetRenderState( D3DRS_ALPHABLENDENABLE,   TRUE );
    device_->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );
    device_->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
    device_->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
    device_->SetRenderState( D3DRS_ALPHAREF,        0x01 );
    device_->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );
    device_->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
    device_->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
    device_->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
    device_->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
    device_->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
    device_->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
    device_->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_POINT);
    device_->SetTextureStageState(0,D3DTSS_MAGFILTER,D3DTEXF_POINT);
    device_->SetTextureStageState(0,D3DTSS_MINFILTER,D3DTEXF_POINT);
  }

  void D3D8GraphicsDevice::SetProjection() {
    device_->SetTransform(D3DTS_PROJECTION,&matrix_projection_);
    D3DXMatrixIdentity(&matrix_view_);
    device_->SetTransform(D3DTS_VIEW,&matrix_view_);
  }

  // Set up the 2D ortho projection 
  void D3D8GraphicsDevice::BuildProjection(size_t width , size_t height) {
    D3DXMATRIX temp;
    D3DXMatrixScaling(&matrix_projection_, 1.0f, -1.0f, 1.0f);
    D3DXMatrixTranslation(&temp, -0.5f, height+0.5f, 0.0f);
    D3DXMatrixMultiply(&matrix_projection_, &matrix_projection_, &temp);
    D3DXMatrixOrthoOffCenterLH(&temp, 0, (float)width, 0, (float)height, 0.0f, 1.0f);
    D3DXMatrixMultiply(&matrix_projection_, &matrix_projection_, &temp);
  }

  int D3D8GraphicsDevice::InitRenderingBuffer() {
    HRESULT hr;
    // Create Vertex Buffer
    hr = device_->CreateVertexBuffer( VERTEX_BUFFER_SIZE*sizeof( xge::graphics::Vertex ),
      D3DUSAGE_WRITEONLY,
      D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1, // FVF
      D3DPOOL_DEFAULT,
      &vbuffer_
      );
    if( FAILED(hr) ) {
      OUTPUT_D3D_INFO(PANIC,true,_xT("IDirect3DDevice::CreateVertexBuffer"),D3D8ErrorCollector::MakeOutputErrorString(hr));
      return CANT_CREATE_VERTEX_BUFFER;
    }
    device_->SetVertexShader( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 );
    device_->SetStreamSource( 0 , vbuffer_ , sizeof( xge::graphics::Vertex ) );
    // Create Index Buffer
    hr = device_->CreateIndexBuffer( VERTEX_BUFFER_SIZE * 3 / 2*sizeof(WORD) ,
      D3DUSAGE_WRITEONLY,
      D3DFMT_INDEX16, // FVF
      D3DPOOL_DEFAULT,
      &ibuffer_
      );
    if( FAILED(hr) ) {
      OUTPUT_D3D_INFO(PANIC,true,_xT("IDirect3DDevice::CreateIndexBuffer"),D3D8ErrorCollector::MakeOutputErrorString(hr));
      return CANT_CREATE_INDEX_BUFFER;
    }
    WORD* index;
    size_t n = 0;

    hr = ibuffer_->Lock( 0,0,
      (BYTE**)&(index),0
      );
    if( FAILED(hr) ) {
      OUTPUT_D3D_INFO(PANIC,true,_xT("IDirect3DIndexBuffer8::Lock!"),D3D8ErrorCollector::MakeOutputErrorString(hr));
      return CANT_CREATE_INDEX_BUFFER;
    }
    // set up index for rendering the QUAD 
    for( size_t i = 0 ; i <VERTEX_BUFFER_SIZE/4 ; ++i  ) {
      *index++=n;
      *index++=n+1;
      *index++=n+2;
      *index++=n+2;
      *index++=n+3;
      *index++=n;
      n+=4;
    }
    ibuffer_->Unlock();
    device_->SetIndices(ibuffer_,0);
    // ok
    return STATUS_NORMAL;
   }

  bool D3D8GraphicsDevice::Snapshot( const char_t* file_name ) {
    // doing the snapshot
    IDirect3DSurface8* surface;
    HRESULT hr = device_->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&surface);
    if( FAILED(hr) ) {
      OUTPUT_D3D_INFO(PANIC,true,_xT("IDirect3DDevice::GetBackBuffer"),D3D8ErrorCollector::MakeOutputErrorString(hr));
      return false;
    }
    hr = 
#ifdef XGE_USE_WIDE_CHAR
      D3DXSaveSurfaceToFileW(file_name,D3DXIFF_BMP,surface,NULL,NULL);
#else
      D3DXSaveSurfaceToFileA(file_name,D3DXIFF_BMP,surface,NULL,NULL);
#endif
    if( FAILED(hr) ) {
      OUTPUT_D3D_INFO(PANIC,true,_xT("D3DXSaveSurfaceToFile"),D3D8ErrorCollector::MakeOutputErrorString(hr));
      surface->Release();
      return false;
    }
    surface->Release();
    return true;
  }

  D3D8GraphicsDevice& D3D8GraphicsDevice::GetSingleton() {
    XASSERT( gDevice != NULL );
    return *gDevice;
  }

  //========================== Implementation of D3D8Texture ===================================
  // D3D8Texture implementation for texture stuff 
  class D3D8Texture : public Texture {
  public:
    virtual ColorStream Lock(
      LockOption usage , 
      const xge::Rect& rect ) ;
    // Every call of the locking operation will force you
    // to call the Unlock function corresponding to that
    // Lock operation .
    virtual bool Unlock();
    static D3D8Texture* CreateTexture( size_t width , size_t height );
    static D3D8Texture* CreateTexture( void* buffer , size_t size , DWORD color_key );
    static void DestroyTexture( D3D8Texture* texture ) ;
    IDirect3DTexture8* texture() {
      return texture_;
    }
  private:
    D3D8Texture(
      size_t image_width , size_t image_height ,
      size_t width , size_t height , IDirect3DTexture8* texture ) 
      : Texture( image_width , image_height , width , height ) , texture_(texture){}
  private:
    IDirect3DTexture8* texture_;
  };

  ColorStream D3D8Texture::Lock(
    LockOption usage,
    const xge::Rect& rect ) {
      D3DSURFACE_DESC desc;
      D3DLOCKED_RECT lRect;
      RECT target_rect , *ptr;
      texture_->GetLevelDesc(0,&desc);
      if( desc.Format != D3DFMT_A8R8G8B8 || 
          desc.Format != D3DFMT_X8R8G8B8 ) 
          return ColorStream(); 
      if( rect.height() == 0.0f &&
          rect.width() == 0.0f ) {
            ptr = NULL ;
            target_rect.left =0;
            target_rect.top =0;
            target_rect.right = width();
            target_rect.bottom= height();
      } else {
        target_rect.left  =  (LONG)rect.left_up_point().x();
        target_rect.top   =  (LONG)rect.left_up_point().y();
        target_rect.right =  (LONG)(target_rect.left + rect.width());
        target_rect.bottom=  (LONG)(target_rect.top+rect.height());
        ptr = &target_rect;
      }

      if( FAILED( texture_->LockRect( 0 , &lRect , ptr , usage == READ_ONLY_LOCK ? D3DLOCK_READONLY : 0 ) ) ) {
        return ColorStream() ;
      }

      return ColorStream(
        lRect.pBits ,
        target_rect.right-target_rect.left,
        target_rect.bottom-target_rect.top );

  }

  bool D3D8Texture::Unlock() {
    texture_->UnlockRect(0);
    return true;
  }

  // Create a d3d texture 
  D3D8Texture* D3D8Texture::CreateTexture( void* buffer , size_t size , DWORD color_key ) {
    D3DFORMAT d3dFormatTry1 , d3dFormatTry2 ;
	  D3DXIMAGE_INFO imageInfo;
    LPDIRECT3DTEXTURE8 texture;
    if( *(DWORD*)buffer == 0x20534444 ) {
      d3dFormatTry1 = D3DFMT_UNKNOWN;
      d3dFormatTry2 = D3DFMT_A8R8G8B8;
    } else {
      d3dFormatTry2 = D3DFMT_UNKNOWN;
      d3dFormatTry1 = D3DFMT_A8R8G8B8;
    }
    // Load the texture from the file
    HRESULT hResult = D3DXCreateTextureFromFileInMemoryEx(
      D3D8GraphicsDevice::GetSingleton().GetDevice(),
      buffer,
      size,
      D3DX_DEFAULT, D3DX_DEFAULT,
      1,
      0,
      d3dFormatTry1,
      D3DPOOL_MANAGED,
      D3DX_FILTER_NONE,
      D3DX_DEFAULT,
      color_key,
      &imageInfo,
      NULL,
      &texture
      );
    // Load again 
    if( FAILED( hResult ) ) {
      // try again 
      hResult = D3DXCreateTextureFromFileInMemoryEx(
      D3D8GraphicsDevice::GetSingleton().GetDevice(),
      buffer,
      size,
      D3DX_DEFAULT, D3DX_DEFAULT,
      1,
      0,
      d3dFormatTry2,
      D3DPOOL_MANAGED,
      D3DX_FILTER_NONE,
      D3DX_DEFAULT,
      color_key,
      &imageInfo,
      NULL,
      &texture
      );
      if( FAILED(hResult) ) {
        OUTPUT_D3D_INFO(PANIC,true,_xT("D3DXCreateTextureFromFileInMemoryEx"), \
          D3D8ErrorCollector::MakeOutputErrorString(hResult));
        return NULL;
      }
    }
    D3DSURFACE_DESC desc;
    texture->GetLevelDesc(0,&desc);
    D3D8Texture* d3dtexture = new D3D8Texture(
      imageInfo.Width,imageInfo.Height,
      desc.Width,desc.Height , texture );
    // ok
    return d3dtexture;
  }

  D3D8Texture* D3D8Texture::CreateTexture( size_t width , size_t height ) {
    LPDIRECT3DTEXTURE8 texturePtr;
    HRESULT hr = 
      D3DXCreateTexture( D3D8GraphicsDevice::GetSingleton().GetDevice(), width, height,
      1,					 
      0,					 
      D3DFMT_A8R8G8B8,	 
      D3DPOOL_MANAGED,	 
      &texturePtr );

    if( FAILED(hr) ) {
      OUTPUT_D3D_INFO(PANIC,true,_xT("D3DXCreateTexture!"),D3D8ErrorCollector::MakeOutputErrorString(hr));
      return NULL;
    } 
    D3DSURFACE_DESC desc;
    texturePtr->GetLevelDesc(0,&desc);
    return new D3D8Texture( desc.Width , desc.Height , desc.Width , desc.Height , texturePtr );
  }

  void D3D8Texture::DestroyTexture( D3D8Texture* texture ) {
    texture->texture_->Release();
    delete texture;
  }


  // D3D8Render target implementation
  class D3D8RenderTexture : public RenderTexture {
  public:
    D3D8RenderTexture( size_t width , size_t height , IDirect3DTexture8 * texture ) 
      : RenderTexture( width , height ) , texture_(texture) , surface_(NULL) {}

    static void Destroy( D3D8RenderTexture* texture ) {
      texture->ReleaseThis();
      delete texture;
    }
    static D3D8RenderTexture* Create( size_t width , size_t height );
    void RestoreThis();
    void ReleaseThis() {
      texture_->Release();
      if( surface_ != NULL ) {
        surface_->Release();
        surface_ = NULL;
      }
    }

    IDirect3DTexture8* texture() const {
      return texture_;
    }

    IDirect3DSurface8* surface() const {
      if( surface_ == NULL ) {
        D3D8RenderTexture* this_ref = const_cast<D3D8RenderTexture*>(this);
        HRESULT hr = this_ref->texture_->GetSurfaceLevel(0,&(this_ref->surface_));
        if( FAILED(hr) ) {
          SYS_LOG(PANIC,true,_xT("IDirect3DSurface8::GetSurfaceLevel"),D3D8ErrorCollector::MakeOutputErrorString(hr));
          return NULL;
        }
      }
      return surface_;
    }
  private:
    IDirect3DTexture8* texture_;
    IDirect3DSurface8* surface_;
  };

  D3D8RenderTexture* D3D8RenderTexture::Create( size_t width , size_t height ) {
    IDirect3DTexture8* texture;
    // create texture
    HRESULT hr = D3DXCreateTexture(
      D3D8GraphicsDevice::GetSingleton().GetDevice(),
      width,height,
      1,D3DUSAGE_RENDERTARGET,
      D3D8GraphicsDevice::GetSingleton().GetPresentParemeter()->BackBufferFormat,
      D3DPOOL_DEFAULT,
      &texture
      );
    if( FAILED(hr)) {
      OUTPUT_D3D_INFO(PANIC,true,_xT("D3DXCreateTexture!"),D3D8ErrorCollector::MakeOutputErrorString(hr));
      return NULL;
    } 
    return new D3D8RenderTexture(width,height,texture);
  }

  void D3D8RenderTexture::RestoreThis() {
    HRESULT hr = D3DXCreateTexture(
      D3D8GraphicsDevice::GetSingleton().GetDevice(),
      width() , height() ,
      1,D3DUSAGE_RENDERTARGET,
      D3D8GraphicsDevice::GetSingleton().GetPresentParemeter()->BackBufferFormat,
      D3DPOOL_DEFAULT,
      &texture_
      );
    // If not restore properly , what to do ?
    if( FAILED(hr) ) 
      SYS_LOG(FATAL,true,_xT("Restore D3DRenderTarget encountered error: Can not create texture again !"));
  }

  class D3D8RenderTarget : public RenderTarget {
  public:
    static D3D8RenderTarget* Create ( size_t width , size_t height ) ;
    static void Destroy( D3D8RenderTarget* target );
    // When the device lost , this function will be called 
    static void Restore();
    static void Release();
    // This is typically very useful for removing the 
    // render target list.STL list have the features
    // of when erase certain one element won't hurt
    // other existed iterator , which is very important
    // for us .
    typedef xge::list< D3D8RenderTarget* >::List  RenderTargetList;
    typedef RenderTargetList::iterator IteratorTag;

    static void Initialize() {
      XASSERT(kRenderTargetList==NULL);
      kRenderTargetList = new RenderTargetList();
      kMutex = new xge::FastLock();
    }

    static void Destroy() {
      XASSERT( kRenderTargetList!=NULL && kMutex != NULL );
      delete kRenderTargetList;
      delete kMutex;
    }

    void RestoreThis();
    void ReleaseThis() {
      ((D3D8RenderTexture*)GetTexture())->ReleaseThis();
      depth_buffer_->Release();
    }

    IDirect3DSurface8* depth_buffer() const {
      return depth_buffer_;
    }

  private:
    static RenderTargetList* kRenderTargetList;
    static xge::FastLock* kMutex;
    typedef xge::AutoLock<xge::FastLock> AutoLock;
    static IteratorTag Add( D3D8RenderTarget* target ) {
      XASSERT(kRenderTargetList!=NULL);
      AutoLock lock(*kMutex);
      kRenderTargetList->push_back(target);
      return --(kRenderTargetList->end());
    }
    static void Remove( const IteratorTag& tag ){
      XASSERT(kRenderTargetList!=NULL);
      AutoLock lock(*kMutex);
      kRenderTargetList->erase(tag);
    }
  private:
    D3D8RenderTarget( size_t width , size_t height , D3D8RenderTexture* texture , IDirect3DSurface8* depth )  
      :RenderTarget(width,height,texture),depth_buffer_(depth){
        tag_ = Add(this);
    }
    ~D3D8RenderTarget() {
      Remove(tag_);
    }
    IDirect3DSurface8* depth_buffer_;
    IteratorTag tag_;
  };
  
  D3D8RenderTarget::RenderTargetList* D3D8RenderTarget::kRenderTargetList = NULL;
  xge::FastLock* D3D8RenderTarget::kMutex = NULL;


  D3D8RenderTarget* D3D8RenderTarget::Create( size_t width , size_t height ) {
    D3D8RenderTexture* texture = D3D8RenderTexture::Create(width,height);
    if( texture == NULL )
      return NULL;
    IDirect3DSurface8* depth_buffer;
    HRESULT hr = D3D8GraphicsDevice::GetSingleton().GetDevice()->CreateDepthStencilSurface(
      width,height,
      D3DFMT_D16,D3DMULTISAMPLE_NONE,
      &depth_buffer
      );
    // create stencil depth buffer
    if( FAILED(hr) ) {
        // destroy the texture here
        D3D8RenderTexture::Destroy(texture);
        OUTPUT_D3D_INFO(PANIC,true,_xT("IDirect3DDevice8::CreateDepthStencilSurface!"), \
          D3D8ErrorCollector::MakeOutputErrorString(hr));
        return NULL;
    }
    return new D3D8RenderTarget(width,height, 
    texture,
    depth_buffer
    );
  }

  void D3D8RenderTarget::Destroy( D3D8RenderTarget* target ) {
    target->depth_buffer_->Release();
    D3D8RenderTexture::Destroy( (D3D8RenderTexture*)( target->GetTexture() ) );
    delete target;
  }

  void D3D8RenderTarget::RestoreThis() {
    ((D3D8RenderTexture*)GetTexture())->RestoreThis();
    HRESULT hr = D3D8GraphicsDevice::GetSingleton().GetDevice()->CreateDepthStencilSurface(
      width(),height(),
      D3DFMT_D16,D3DMULTISAMPLE_NONE,
      &depth_buffer_
      );
    if( FAILED(hr) )
      SYS_LOG(FATAL,true,_xT("Can not restore D3DRenderTarget:can not create depth buffer!"));
  }


  void D3D8RenderTarget::Restore() {
    XASSERT(kRenderTargetList!=NULL);
    AutoLock lock(*kMutex);
    for( IteratorTag ib = kRenderTargetList->begin() , 
      ie = kRenderTargetList->end() ; ib != ie ; ++ib ) {
      (*ib)->RestoreThis();
    }
  }

  void D3D8RenderTarget::Release() {
    XASSERT(kRenderTargetList!=NULL);
    AutoLock lock(*kMutex);
    for( IteratorTag ib = kRenderTargetList->begin() , 
      ie = kRenderTargetList->end() ; ib != ie ; ++ib ) {
        (*ib)->ReleaseThis();
    }
  }


  // ===================== Implementation of D3DGraphicsDevice interface ============================
  // Texture ==================================
  Texture* D3D8GraphicsDevice::CreateTexture( void* raw_buffer , size_t size , const Color& color_key ) {
    // create texture using raw buffer
    return D3D8Texture::CreateTexture(raw_buffer,size,color_key.hardware_color());
  }

  Texture* D3D8GraphicsDevice::CreateTexture( size_t width , size_t height ) {
    return D3D8Texture::CreateTexture( width , height ); 
  }

  bool D3D8GraphicsDevice::DestroyTexture( Texture* texture ) {
    D3D8Texture::DestroyTexture((D3D8Texture*)texture);
    return true;
  }
  // Render Target ============================
  RenderTarget* D3D8GraphicsDevice::CreateRenderTarget( size_t width , size_t height ) {
    return D3D8RenderTarget::Create(width,height);
  }

  bool D3D8GraphicsDevice::DestroyRenderTarget( RenderTarget* target ) {
    XASSERT( target != current_target_ );
    D3D8RenderTarget::Destroy( (D3D8RenderTarget*)target );
    return true;
  }

  int D3D8GraphicsDevice::SetRenderTarget(  RenderTarget* target  ) {
    IDirect3DSurface8* renderBuffer ;
    IDirect3DSurface8* depthBuffer ;
    // If the current target is what we want
    // keep it as default target ;otherwise
    // change it( Keeping the render state 
    // stable ).
    if( current_target_ != target ) {
      // Specifying the render target now 
      if( target != NULL ) {
        // the user has specified the target now
        // You should use com ptr to make it safe !
        D3D8RenderTarget* d3d8RenderTarget = (D3D8RenderTarget*)(target);
        depthBuffer = d3d8RenderTarget->depth_buffer();
        D3D8RenderTexture* d3d8RenderTexture = (D3D8RenderTexture*)d3d8RenderTarget->GetTexture();
        renderBuffer = d3d8RenderTexture->surface();
        if( renderBuffer == NULL )
          return CANT_GET_SURFACE;
        // building the projection here
        BuildProjection(d3d8RenderTarget->width(),d3d8RenderTarget->height());
      } else {
        renderBuffer = screen_surface_;
        depthBuffer  = screen_depth_ ;
        BuildProjection(window_->width(),window_->height());
      }
      // now sets the current_target_ to target now 
      device_->SetRenderState( D3DRS_ZENABLE , D3DZB_TRUE );
      device_->SetRenderTarget(renderBuffer,depthBuffer);
      SetProjection();
      current_target_ = (D3D8RenderTarget*)target;
    }

    return STATUS_NORMAL;
  }
  // 
  int D3D8GraphicsDevice
    ::BeginScene( int mode  , int primitive_type , RenderTarget* target , 
    Texture* texture , int blending_mode ,size_t wait_time_out  ) {
    bool bUseTarget = false;
      // Testing the device is ok currently 
    HRESULT hr = device_->TestCooperativeLevel();
    if( hr == D3DERR_DEVICELOST ) {
      // doing the recording here
      OUTPUT_D3D_INFO(PANIC,true,_xT("IDirect3DDevice8::TestCooperativeLevel"),D3D8ErrorCollector::MakeOutputErrorString(hr));
      // lost 
      Sleep(wait_time_out);
      hr = device_->TestCooperativeLevel();
      if( hr == D3DERR_DEVICELOST ) {
        OUTPUT_D3D_INFO(PANIC,true,_xT("IDirect3DDevice8::TestCooperativeLevel"),D3D8ErrorCollector::MakeOutputErrorString(hr));
        return DEVICE_LOST;
      }
      // restore the current device now
      if( hr == D3DERR_DEVICENOTRESET ) {
        DeviceRestore();
      }
    } else if ( hr == D3DERR_DEVICENOTRESET ) {
      DeviceRestore();
    }
    // set the render target stuff
    int ret = SetRenderTarget(target);
    if( ret != STATUS_NORMAL ) 
      return ret;

    hr = device_->BeginScene();
    if( FAILED(hr) ) {
      OUTPUT_D3D_INFO(PANIC,true,_xT("IDirect3DDevice8::BeginScene"),D3D8ErrorCollector::MakeOutputErrorString(hr));
      return CANT_BEGIN_SCENE;
    }
    XASSERT( vertex_buffer_ptr_ == NULL && vertex_buffer_num_ == 0 );
    // lock the buffer and write into the buffer
    vbuffer_->Lock(0,0,(BYTE**)(&vertex_buffer_ptr_),0);
    cur_primitive_type_ = PRIMITIVE_QUAD;
    // using the default blend mode 
    cur_blend_mode_ = BLEND_DEFAULT;
    cur_mode_ = mode;
    if( mode = MODE_BATCH ) {
      XASSERT( primitive_type == PRIMITIVE_LINE || primitive_type == PRIMITIVE_QUAD );
      cur_primitive_type_ = primitive_type;
      if( cur_primitive_type_ == PRIMITIVE_LINE 
        || current_texture_ != NULL )
        SetTexture(NULL);
      else 
        if( current_texture_ != texture )
          SetTexture(texture);
      SetBlendingMode(blending_mode);
    }
    return STATUS_NORMAL;
  }


  void D3D8GraphicsDevice::Batch( bool end ) {
    if( vertex_buffer_ptr_ != NULL ) {
      vbuffer_->Unlock();
      if( vertex_buffer_num_ != 0 ) {
        if( cur_primitive_type_ == PRIMITIVE_LINE ) {
          XVERYFIY( SUCCEEDED( device_->DrawPrimitive(D3DPT_LINELIST,0,vertex_buffer_num_) ) ) ;
        } else {
          XVERYFIY( SUCCEEDED( device_->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,vertex_buffer_num_<< 2 ,0 ,vertex_buffer_num_<<1 ) ) );
        }
        vertex_buffer_num_ = 0;
      } 
      if( end ) {
        vertex_buffer_ptr_ = NULL ;
      } else {
        vbuffer_->Lock( 0 ,0 , (BYTE**)(&vertex_buffer_ptr_) , 0 );
      }
    }
  }

  int D3D8GraphicsDevice::EndScene( bool sync ) {
    Batch( true );
    XVERYFIY(SUCCEEDED(device_->EndScene()));
    // if the user want to sync the rendering
    // calling the Presents method will enable
    // the stuff glFlush() , however , the
    // user may not want to have this operation
    if( current_target_ == NULL || sync ) 
      device_->Present(NULL,NULL,NULL,NULL);

    return STATUS_NORMAL;
  }

  void D3D8GraphicsDevice::SetBlendingMode( int mode ) {
    // doing the mode setting now
    if( (mode & BLEND_ALPHA) != (cur_blend_mode_&BLEND_ALPHA) ) {
      if( mode & BLEND_ALPHA ) 
        device_->SetRenderState( D3DRS_DESTBLEND , D3DBLEND_INVSRCALPHA );
      else 
        device_->SetRenderState( D3DRS_DESTBLEND , D3DBLEND_ONE );
    } 

    if( (mode & BLEND_ZBUFFER ) != (cur_blend_mode_ & BLEND_ZBUFFER) ) {
      if( mode&BLEND_ZBUFFER )
        device_->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
      else
        device_->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
    }

    if( (mode&BLEND_ADD) != (cur_blend_mode_ & BLEND_ADD ) ) {
      if( mode&BLEND_ADD )
        device_->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_ADD);
      else
        device_->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE);
    }

    cur_blend_mode_ = mode;
  }

  void D3D8GraphicsDevice::SetClippingArea( const Rect& rect ) {
    // TODO:: Add Implementation Here
    return;
  }

  void D3D8GraphicsDevice
    ::SetGlobalTransformation( 
    float ax,
    float ay,
    float horizontal_offset ,
    float vertical_offset,
    float horizontal_scale,
    float vertical_scale,
    float rotation
    ){
      // TODO:: Add Implementation Here
  }

  void D3D8GraphicsDevice::RenderQuad( const Quad& quad ) {
    XASSERT( vertex_buffer_ptr_ != NULL );
    if( vertex_buffer_ptr_ )
      // rendering the quad 
      if( cur_mode_ == Graphics2D::MODE_NORMAL ) {
        // batch now or later 
        if( cur_primitive_type_ != PRIMITIVE_QUAD ||
            cur_blend_mode_ != quad.blending_type ||
            current_texture_!= quad.texture ||
            vertex_buffer_num_ >= VERTEX_BUFFER_SIZE / PRIMITIVE_QUAD_SIZE ) {
              Batch(false);
        }
        // do not testing the texture and bending mode 
        if( cur_blend_mode_ != quad.blending_type ) {
          SetBlendingMode(quad.blending_type);
        }
        // setting the texture stuff
        if( current_texture_ != quad.texture ) {
          SetTexture(quad.texture);
        }
        cur_primitive_type_ = PRIMITIVE_QUAD;
        // now do the rendering batch here 
        memcpy(&(vertex_buffer_ptr_[vertex_buffer_num_*PRIMITIVE_QUAD_SIZE]),quad.vertex,sizeof(Vertex)*PRIMITIVE_QUAD_SIZE);
        vertex_buffer_num_++;
      } else {
        // testing the blending mode is alright or not 
        XASSERT( cur_primitive_type_ == PRIMITIVE_QUAD );
        if( cur_primitive_type_ != PRIMITIVE_QUAD )
          return;
        if( vertex_buffer_num_ >= VERTEX_BUFFER_SIZE / PRIMITIVE_QUAD_SIZE )
          Batch(false);
        memcpy(&(vertex_buffer_ptr_[vertex_buffer_num_*PRIMITIVE_QUAD_SIZE]),quad.vertex,sizeof(Vertex)*PRIMITIVE_QUAD_SIZE);
        vertex_buffer_num_++;
      }
  }

  void D3D8GraphicsDevice::SetTexture( Texture* texture ) {
    if( texture != NULL )
      device_->SetTexture(0,((D3D8Texture*)(texture))->texture());
    else 
      device_->SetTexture(0,NULL);
    current_texture_ = (D3D8Texture*)texture;
  }

  void D3D8GraphicsDevice
    ::RenderLine( const Point& p1,const Point& p2,float zbuffer , int blending , const Color& color1, const Color& color2 ) {
#if 1
      XASSERT( vertex_buffer_ptr_ != NULL );
      if( vertex_buffer_ptr_ )
        if( cur_mode_ == Graphics2D::MODE_NORMAL ) {
          // batch now or later 
          if( cur_primitive_type_ != PRIMITIVE_LINE ||
            cur_blend_mode_ != blending ||
            current_texture_ != NULL ||
            vertex_buffer_num_ >= VERTEX_BUFFER_SIZE / PRIMITIVE_LINE_SIZE ) {
              Batch(false);
          }
          if( cur_blend_mode_ != BLEND_DEFAULT )
            SetBlendingMode(BLEND_DEFAULT);
          if( current_texture_ != NULL ) {
            SetTexture(NULL);
          }
          cur_primitive_type_ = PRIMITIVE_LINE;
          const size_t index = vertex_buffer_num_ * PRIMITIVE_LINE_SIZE ;
          vertex_buffer_ptr_[index].x = p1.x();
          vertex_buffer_ptr_[index].y = p1.y();
          vertex_buffer_ptr_[index+1].x=p2.x();
          vertex_buffer_ptr_[index+1].y=p2.y();
          vertex_buffer_ptr_[index].z = vertex_buffer_ptr_[index+1].z = zbuffer;
          vertex_buffer_ptr_[index].color = color1.hardware_color();
          vertex_buffer_ptr_[index+1].color = color2.hardware_color();
          vertex_buffer_ptr_[index].tx= vertex_buffer_ptr_[index].ty =
          vertex_buffer_ptr_[index+1].tx = vertex_buffer_ptr_[index+1].ty = 0.0f;
          ++vertex_buffer_num_;
        } else {
          XASSERT( cur_primitive_type_ == PRIMITIVE_LINE );
          if( cur_primitive_type_ != PRIMITIVE_LINE )
            return;
          if( vertex_buffer_num_ >= VERTEX_BUFFER_SIZE / PRIMITIVE_LINE_SIZE )
            Batch(false);
          cur_primitive_type_ = PRIMITIVE_LINE;
          const size_t index = vertex_buffer_num_ * PRIMITIVE_LINE_SIZE ;
          vertex_buffer_ptr_[index].x = p1.x();
          vertex_buffer_ptr_[index].y = p1.y();
          vertex_buffer_ptr_[index+1].x=p2.x();
          vertex_buffer_ptr_[index+1].y=p2.y();
          vertex_buffer_ptr_[index].z = vertex_buffer_ptr_[index+1].z = zbuffer;
          vertex_buffer_ptr_[index].color = color1.hardware_color();
          vertex_buffer_ptr_[index+1].color = color2.hardware_color();
          vertex_buffer_ptr_[index].tx= vertex_buffer_ptr_[index].ty =
            vertex_buffer_ptr_[index+1].tx = vertex_buffer_ptr_[index+1].ty = 0.0f;
          ++vertex_buffer_num_;
        }
#else
        Vertex vtx[2];
#define ASSIGN(Slot,Pt) \
  Slot.x = Pt.x();Slot.y=Pt.y();Slot.z=zbuffer;Slot.tx=Slot.ty=0.0f
        ASSIGN(vtx[1],p2);
        ASSIGN(vtx[0],p1);
#undef ASSIGN
        device_->DrawPrimitiveUP(D3DPT_LINELIST,1,&vtx[0],sizeof(Vertex));
#endif
  }

  void D3D8GraphicsDevice::Clear( const Color& color ) {
    device_->Clear(0,NULL,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,color.hardware_color(),1.0f,0);
  }

  // =============================== D3D8GraphicsWindow ============================================
  bool D3D8GraphicsWindow::Snapshot( const String& file_name ) {
    return D3D8GraphicsDevice::GetSingleton().Snapshot( file_name.c_str() );
  }
  // Main windows message procedure

  LRESULT CALLBACK D3D8GraphicsWindow::WndProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) {
#define WINDOW ((D3D8GraphicsWindow*)(D3D8GraphicsDevice::GetSingleton().GetGraphicsWindow()))

    bool active;
    switch( msg ) {
      // Windows Message Create
    case WM_CREATE:
      SYS_LOG(INFO,false,_xT("The window has been created!"));
      return FALSE;
      // Windows Message Paint
    case WM_PAINT:
      PAINTSTRUCT ps;
      BeginPaint( WINDOW->hwnd() , &ps);
      EndPaint( WINDOW->hwnd() , &ps);
      break;
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    case WM_ACTIVATE:
      // testing active message here
      // WM_ACTIVATE fActive = LOWORD(wParam); 
      // fMinimized = (BOOL)HIWORD(wParam);
      // hwndPrevious = (HWND) lParam;
      active = (LOWORD(wparam) != WA_INACTIVE) && (HIWORD(wparam) == FALSE);
      if( active )
        WINDOW->InvokeFocusGain();
      else
        WINDOW->InvokeFocusLost();
      break;

    case WM_SIZE:
      // since we have forced the window not able to
      // maximize and minimize , we can only receive
      // the message SIZE_RESTORED 
      if( wparam == SIZE_RESTORED ) {
        // doing the stuff here
        D3D8GraphicsDevice::GetSingleton().OnResize( LOWORD(lparam) , HIWORD(lparam) );
      }
      break;
    case WM_SYSCOMMAND:
      if( wparam == SC_CLOSE ) {
        WINDOW->InvokeOnExit();
      }
      break;
    default:
      break;
    }// switch over
    return DefWindowProc(hwnd, msg, wparam, lparam);
  }
#undef WINDOW
  // =============================== D3D8GraphicsDevice CTOR Implementation =========================

  D3D8GraphicsDevice::D3D8GraphicsDevice(
    size_t  width, size_t height , bool fullscreen , const String& caption , const String& resource_title ) {
    XASSERT(gDevice==NULL);
    gDevice= this;
    // doing the ctor stuff here
    direct3d8_ = NULL;
    device_ = NULL ;
    direct3d8_ = NULL;
    ibuffer_ = NULL;
    vbuffer_ = NULL;
    screen_depth_ = NULL;
    screen_surface_ = NULL;
    vertex_buffer_ptr_ = NULL;
    vertex_buffer_num_ = 0;
    current_texture_= NULL;
    current_target_ = NULL;
    window_ = new D3D8GraphicsWindow( this );
    // now create the window now 
    int status_code = window_->Create(
      width,height,
      caption,resource_title, fullscreen );
    if( status_code != STATUS_NORMAL ) {
      last_status_code_ = status_code;
      SYS_LOG(PANIC,true,_xT("The D3D8GraphicsWindow initialized failed!"));
      gDevice = NULL;
      return;
    }
    // initialize error collector
    D3D8ErrorCollector::InitializeOutputErrorString();
    // get the status code of graphics card
    status_code = CreateGraphics();
    if( status_code != STATUS_NORMAL ) {
      last_status_code_ = status_code;
      SYS_LOG(PANIC,true,_xT("The D3D8GraphicsDevice initialized failed!"));
      gDevice = NULL;
      return;
    }
    // initialize render target
    D3D8RenderTarget::Initialize();
    last_status_code_ = STATUS_NORMAL;
    SYS_LOG(INFO,false,_xT("The D3D8GraphicsDevice initialized successfully!"));
  }

  D3D8GraphicsDevice::~D3D8GraphicsDevice() {
    // destroy the window
    window_->Destroy();
    delete window_;
    // destroy myself
    DestroyGraphics();
    D3D8RenderTarget::Destroy();
    D3D8ErrorCollector::Destroy();
    gDevice = NULL;
  }

  void D3D8GraphicsDevice::OnRestore() {
    
    DeviceRestore();
  }

  void D3D8GraphicsDevice::OnResize( size_t width , size_t height ) {
    // do nothing here
    // since the resize won't have any useful information 
  }

  void D3D8GraphicsDevice::DeviceRestore() {
    screen_depth_->Release();
    screen_surface_->Release();
    D3D8RenderTarget::Release();
    ibuffer_->Release();
    vbuffer_->Release();
    device_->SetStreamSource(NULL,0,sizeof(Vertex));
    device_->SetIndices(NULL,0);
    device_->Reset(&present_par_);
    // Restore now
    InitRenderingBuffer();
    D3D8RenderTarget::Restore();
    device_->GetRenderTarget(&screen_surface_);
    device_->GetDepthStencilSurface(&screen_depth_);
    SetRenderState();

  }




}//namespace 

namespace xge{
namespace graphics{

  Graphics2D* CreateGraphics2DDevice(
    int* error_code , size_t width , size_t height , bool fullscreen , 
    const String& caption , const String& icon_res_string /* = String */ , DeviceType type ) {
      XASSERT(gDevice==NULL);
      D3D8GraphicsDevice* device = new D3D8GraphicsDevice(width,height,fullscreen,caption,icon_res_string);
      if( device->last_status_code() != STATUS_NORMAL ) {
        delete device;
        if( error_code )
          *error_code = device->last_status_code();
        return NULL;
      } 

      return device;
  }


  bool DestroyGraphics2DDevice( Graphics2D* g ) {
    if( g==gDevice ) {
      delete gDevice;
      gDevice = NULL;
      return true;
    }
    return false;
  }


}//namespace graphics
}//namespace xge


#undef OUTPUT_D3D_INFO




